#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "gdst_array.h"
#include "view.h"
#include "paxos.h"

/*

state:
  n_a, v_a: highest value and proposal # which node has accepted
  n_h: highest proposal # seen in a prepare
  my_n: the last proposal # the node has used in this round of Paxos
  vid_h: highest view number we have accepted
  views: map of past view numbers to values
  done: leader says agreement was reached, we can start new view

on each view change, initialize state:
  n_a = 0
  n_h = 0
  my_n = 0
  v_a = () // empty list

Paxos Phase 1
  a node (maybe more than one...) decides to be leader (need not be in current view):
    my_n = max(n_h, my_n)+1, append node ID  // unique proposal number
    done = false
    sends prepare(vid_h+1, my_n) to all nodes in {views[vid_h], initial contact node, itself}
  if node receives prepare(vid, n):
    if vid <= vid_h:
      return oldview(vid, views[vid])
    else if n > n_h:
      n_h = n
      done = false
      return prepareres(n_a, v_a)
    else:
      return reject()

Paxos Phase 2
  if leader gets oldview(vid, v):
    views[vid] = v
    vid_h = vid
    view change
    restart paxos
  else if leader gets reject():
    delay and restart paxos
  else if leader gets prepareres from majority of nodes in views[vid_h]:
    if any prepareres(n_i, v_i) exists such that v_i is not empty:
      v = non-empty value v_i corresponding to highest n_i received
    else leader gets to choose a value:
      v = set of pingable nodes (including self)
    send accept(vid_h+1, my_n, v) to all responders
  else:
    delay and restart paxos
  if node gets accept(vid, n, v):
    if vid <= vid_h:
      return oldview(vid, views[vid])
    else if n >= n_h:
      n_a = n
      v_a = v
      return acceptres()
    else
      return reject()

Paxos Phase 3
  if leader gets oldview(vid, v):
    views[vid] = v
    vid_h = vid
    view change
    restart paxos
  else if leader gets acceptres from a majority of nodes in views[vid_h]:
    send decide(vid_h+1, v_a) to all (including self)
  else:
    delay and restart paxos
  if node gets decide(vid, v):
    if vid <= vid_h:
      return oldview(vid, views[vid])
    else:
      done = true
      primary is lowest-numbered node in v
      views[vid] = v
      vid_h = vid
      view change

*/

typedef struct propid_t
{
	uint32_t n;
	/*add something that makes it unique -- node identifier*/
}propid_t;

typedef struct paxos_t
{
	/*array of known nodes <view_t>*/
	array_p nodes;
	/*highest view number accepted*/
	uint32_t vid_h;
	/*last proposal num used in current round of paxos*/  
	propid_t my_n;
	/*highest proposal num seen in an prepare*/
	propid_t n_h;
	/*proposal number that node accepted*/
	propid_t n_a;
	/*array of nodes that accepted the value*/
	array_p v_a;

	/*agreement reached, start a new view*/
	uint8_t  done;
	
	/*add views history*/
}paxos_t;

paxos_p paxos_new(char *id)
{
	paxos_p inst;

	inst = (paxos_p)calloc(1, sizeof(paxos_t));
	if (!inst)
		return NULL;
	/*init here*/
	inst->nodes = gdst_arraynew(1, sizeof(struct view_t));
	/*end init*/
	return inst;
}

void paxos_free(paxos_p *self_p)
{
	if (self_p && *self_p)
	{
		paxos_p self = *self_p;
		/*cleanup here*/
		gdst_arrayfree(&self->nodes);
		/*end cleanup*/
		free(self);
		*self_p = NULL;
	}
}
